#include "pwext.hpp"
#include <list>
#include <stdio.h>

class data;
void exchange_data(std::list<data *> &dlist)
{
	// DEBUG {
	fprintf(stderr, "---\n");
	// DEBUG }
}

// data used by the workers, its contents is not important here
class data;

class barrier 
{
public:
	barrier(int count);

	// May be called only by the control thread, to adjust
	// the number of workers
	int add_workers(int add);

	// Workers call this method to synchronize on the barrier
	void sync();

	// Control calls this method to wait for the barrier
	// to be reached by all the workers
	void wait();

	// Control calls this method to release the workers from the
	// barrier
	void signal();
};

// [[ex03ch]]
// DEBUG descr Usage of a barrier with an explicit control thread.
class worker : public pw::pwthread
{
public:
	worker(data *d, barrier *b) :
		data_(d), barrier_(b), stop_(false)
	{ }

	virtual void *execute()
	{
		while (!stop_) {
			// ... perform the computations ...
	// DEBUG {
			fprintf(stderr, "worker %p step data %p\n", this, data_);
	// DEBUG }
			barrier_->sync();
		}
		return 0;
	}

	void stop()
	{
		stop_ = true;
	}

protected:
	data *data_;
	barrier *barrier_;
	bool stop_;
};

// the function running in the control thread,
// processes a list of data in parallel
void control(std::list<data *> &dlist, int steps)
{
	barrier bar(0);
	std::list <worker *> workers;

	// prepare the data elements
	exchange_data(dlist);

	// start the workers
	for (std::list<data *>::iterator it = dlist.begin(); 
			it != dlist.end(); ++it) {
		worker *w = new worker(*it, &bar);
		workers.push_back(w);
		bar.add_workers(1);
		w->start();
	}

	for (int i = 1; i < steps; i++) {
		bar.wait();
		exchange_data(dlist);
		bar.signal();
	}

	// all the calculations are done
	bar.wait();
	for (std::list<worker *>::iterator it = 
			workers.begin(); it != workers.end(); ++it) 
		(*it)->stop();
	bar.signal();

	for (std::list<worker *>::iterator it = 
			workers.begin(); it != workers.end(); ++it) {
		(*it)->join();
		delete (*it);
	}
}
// END
